﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using HtmlAgilityPack;

namespace JoelAbrahamsson.Web.Common.HtmlFiltering
{
    public class HtmlFilter
    {
        Dictionary<string, IHtmlTagReplacer> _allowedTagReplacements;

        public List<string> RemoveChildrenOfTags { get; set; }

        public List<ITextNodeFilter> TextNodeFilters { get; set; }

        public HtmlFilter(Dictionary<string, IHtmlTagReplacer> allowedTagReplacements)
        {
            _allowedTagReplacements = allowedTagReplacements;
            RemoveChildrenOfTags = new List<string> { "script", "style" };
            TextNodeFilters = new List<ITextNodeFilter>();
        }

        public string FilterHtml(string html)
        {
            HtmlDocument document = new HtmlDocument();
            document.LoadHtml(html);

            StringBuilder buffer = new StringBuilder();
            Process(document.DocumentNode, buffer, _allowedTagReplacements);

            return buffer.ToString();
        }

        private void Process(HtmlNode node, StringBuilder buffer, Dictionary<string, IHtmlTagReplacer> allowedTagReplacements)
        {
            switch (node.NodeType)
            {
                case HtmlNodeType.Text:
                    AppendTextNode(node, buffer);
                    break;
                case HtmlNodeType.Element:
                case HtmlNodeType.Document:
                    AppendTag(node, buffer, allowedTagReplacements);
                    break;
            }
        }

        private void AppendTag(HtmlNode node, StringBuilder buffer, Dictionary<string, IHtmlTagReplacer> allowedTagReplacements)
        {
            bool allowedTag = allowedTagReplacements.ContainsKey(node.Name.ToLower());
            if (allowedTag)
                buffer.Append(allowedTagReplacements[node.Name.ToLower()].ReplaceOpeningTag(node));
            if (!RemoveChildrenOfTags.Contains(node.Name.ToLower()))
                foreach (HtmlNode childNode in node.ChildNodes)
                    Process(childNode, buffer, allowedTagReplacements);
            if (allowedTag)
                buffer.Append(allowedTagReplacements[node.Name.ToLower()].ReplaceClosingTag(node));
        }

        private void AppendTextNode(HtmlNode node, StringBuilder buffer)
        {
            string text = HttpUtility.HtmlEncode(((HtmlTextNode)node).Text);
            
            foreach (ITextNodeFilter filter in TextNodeFilters)
            {
                text = filter.Filter(text, node);
            }

            buffer.Append(text);
        }
    }
}